package br.jus.tjpe.converter;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.faces.component.UIComponent;
import javax.faces.component.UISelectItem;
import javax.faces.component.UISelectItems;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.model.SelectItem;
import javax.faces.model.SelectItemGroup;

public class EntityConverter implements Converter {

	/** Indice do objeto. */
	private int index = -1;

	/** {@inheritDoc} */
	public Object getAsObject(FacesContext ctx, UIComponent component, String value) {
		Object item = null;

		if (value != null && !value.trim().isEmpty()) {
			SelectItem selectedItem = this.getSelectedItemByIndex(component, Integer.parseInt(value));

			if (selectedItem != null) {
				item = selectedItem.getValue();
			}
		}

		return item;
	}

	/** {@inheritDoc} */
	public String getAsString(FacesContext ctx, UIComponent component, Object value) {
		index = index + 1;
		return String.valueOf(index);
	}

	/**
	 * Seleciona o objeto através do índice passado.
	 * 
	 * @param component
	 *            O componente a recuperar os dados.
	 * @param index
	 *            Indice para recuperar o objeto selecionado.
	 * @return O Objeto selecionado.
	 */
	protected SelectItem getSelectedItemByIndex(UIComponent component, int index) {

		List<SelectItem> items = this.getSelectItems(component);
		int size = items.size();

		if (index > -1 && size > index) {
			return items.get(index);
		}

		return null;
	}

	/**
	 * Recupera a lista de todos os itens.
	 * 
	 * @param component
	 *            O componente a recuperar os dados.
	 * @return Uma lista de itens.
	 */
	protected List<SelectItem> getSelectItems(UIComponent component) {

		List<SelectItem> items = new ArrayList<SelectItem>();

		int childCount = component.getChildCount();

		if (childCount != 0) {

			List<UIComponent> children = component.getChildren();

			for (UIComponent child : children) {

				if (child instanceof UISelectItem) {
					this.addSelectItem((UISelectItem) child, items);
				} else if (child instanceof UISelectItems) {
					this.addSelectItems((UISelectItems) child, items);
				}
			}
		}

		return items;
	}

	/**
	 * Adiciona um item na lista.
	 * 
	 * @param uiItem
	 *            O Item a ser recuperado.
	 * @param items
	 *            A lista de itens a ser adicionado.
	 * 
	 */
	protected void addSelectItem(UISelectItem uiItem, List<SelectItem> items) {

		boolean isRendered = uiItem.isRendered();
		if (!isRendered) {
			items.add(null);
			return;
		}

		Object value = uiItem.getValue();
		SelectItem item;

		if (value instanceof SelectItem) {
			item = (SelectItem) value;
		} else {
			Object itemValue = uiItem.getItemValue();

			if (itemValue == null) {
				itemValue = "";
			}

			String itemLabel = uiItem.getItemLabel();

			if (itemLabel == null) {
				itemLabel = "";
			}

			item = new SelectItem(itemValue, itemLabel, uiItem.getItemDescription(), uiItem.isItemDisabled());
		}

		items.add(item);
	}

	/**
	 * Adiciona uma lista de itens a lista definitiva de item.
	 * 
	 * @param uiItems
	 *            Os itens a ser adicionado.
	 * @param items
	 *            A lista de itens a ser adicionado.
	 */
	@SuppressWarnings("unchecked")
	protected void addSelectItems(UISelectItems uiItems, List<SelectItem> items) {

		boolean isRendered = uiItems.isRendered();
		if (!isRendered) {
			items.add(null);
			return;
		}

		Object value = uiItems.getValue();

		if (value instanceof SelectItem) {
			items.add((SelectItem) value);
		} else if (value instanceof Object[]) {
			Object[] array = (Object[]) value;
			for (int i = 0; i < array.length; i++) {
				if (array[i] instanceof SelectItemGroup) {
					resolveAndAddItems((SelectItemGroup) array[i], items);
				} else {
					items.add((SelectItem) array[i]);
				}
			}
		} else if (value instanceof Collection) {
			Iterator<SelectItem> iter = ((Collection<SelectItem>) value).iterator();
			SelectItem item;
			while (iter.hasNext()) {
				item = iter.next();
				if (item instanceof SelectItemGroup) {
					resolveAndAddItems((SelectItemGroup) item, items);
				} else {
					items.add(item);
				}
			}
		} else if (value instanceof Map) {
			for (Map.Entry<Object, Object> entry : ((Map<Object, Object>) value).entrySet()) {
				Object label = entry.getKey();
				String labelString = null;

				if (label != null) {
					label = label.toString();
				}

				SelectItem item = new SelectItem(entry.getValue(), labelString);

				if (item instanceof SelectItemGroup) {
					resolveAndAddItems((SelectItemGroup) item, items);
				} else {
					items.add(item);
				}
			}
		}
	}

	/**
	 * Metódo recursivo para adicionar itens.
	 * 
	 * @param group
	 *            Item do item SelectItemGroup a ser percorrido.
	 * @param items
	 *            A lista de itens a ser adicionado.
	 */
	protected void resolveAndAddItems(SelectItemGroup group, List<SelectItem> items) {
		for (SelectItem item : group.getSelectItems()) {
			if (item instanceof SelectItemGroup) {
				resolveAndAddItems((SelectItemGroup) item, items);
			} else {
				items.add(item);
			}
		}
	}

}